﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace FastLimitFlow
{
    public class LeakageBucket : IRateLimit
    {
        //令牌队列
        public ConcurrentQueue<object> TokenQueue { set; get; }
        //最大令牌数
        public int MaxTokenSize { set; get; }
        //令牌丢出速率 个/秒，即流量
        public int TokenProduceRate { set; get; }
        private CancellationTokenSource cancelToken;
        private Task task;

        public LeakageBucket(int MaxTokenSize, int TokenProduceRate)
        {
            cancelToken = new CancellationTokenSource();
            TokenQueue = new ConcurrentQueue<object>();
            this.MaxTokenSize = MaxTokenSize;
            this.TokenProduceRate = TokenProduceRate;
            for (int i = 0; i < MaxTokenSize; i++)
            {
                TokenQueue.Enqueue(new object());
            }
            task = Task.Factory.StartNew(new Action(TokenProcess), cancelToken.Token);
            //初始为满桶
            //漏桶的限流原理为
            //根据一定速率抛出令牌
            //如果固定消耗率为2个/秒，则抛出一个休眠0.5秒
            //可用条件为，队列未满，能继续添加令牌，保持丢出速率，就能限制一定时间内的投入率，达到限流的目的
        }

        public void TokenProcess()
        {
            //每秒丢出令牌速度，比如速率2个/秒，丢出1一个需要等待0.5秒再丢出
            int sleep = 1000 / TokenProduceRate;
            DateTime start = DateTime.Now;
            while (cancelToken.Token.IsCancellationRequested == false)
            {
                try
                {
                    //如果队列不为空，抛出令牌
                    if (!TokenQueue.IsEmpty)
                    {
                        object result = null;
                        TokenQueue.TryDequeue(out result);
                    }
                }
                catch (Exception)
                {

                    throw;
                }
                finally
                {
                    Thread.Sleep(sleep);
                }
            }
        }

        public void Dispose()
        {
            cancelToken.Cancel();
        }

        //是否有令牌可用
        public bool IsHaveToken()
        {
            if (TokenQueue.Count >= MaxTokenSize)
            {
                return false;
            }
            else
            {
                TokenQueue.Enqueue(new object());
                return true;
            }

        }
    }
}
